// Provide a function to get elements by class name an essential function missing in both major browsers

function getElementsByClassName(node, cls, tag) {
	var i, j, re, elm, elms, ismatch;
	var results = new Array();
	if (!document.getElementsByTagName) return results;
	// check type and validity of cls param
	if (typeof cls == 'string') cls = cls.split(' ');
	if (cls.length == 0) return results;
	// covert array of classnames to array of regexp objects matching classnames
	// doing this allows RegExp objects to be created just once
	for (i=0; i<cls.length; i++) {
		cls[i] = new RegExp('(^| )'+cls[i]+'( |$)');
	}
	// grab all the elements in node of type tag
	if (tag == null) tag = '*';
	elms = (tag == '*' && document.all) ? document.all : node.getElementsByTagName(tag); // IE5 does not like getElementsByTagName('*')
	// find matching elements
	for (i=0; elm=elms[i]; i++) {
		for (j=0; re=cls[j]; j++) {
			ismatch = true;
			if (!re.test(elm.className)) {
				ismatch = false;
				break;
			}
		}
		if (ismatch) results[results.length] = elm; // no Array.push in IE5
	}
	return results;
}

// Standardise the Text content functions accross browsers

function getText(control) 
{ 
    if (document.all) 
    { 
        return control.innerText; 
    } 
    else 
    { 
        return control.textContent; 
    } 
} 

function setText(control, value) 
{ 
    if (document.all) 
    { 
        control.innerText = value; 
    } 
    else 
    { 
        control.textContent = value; 
    } 
}

// A stack of functions to run onload/domready

startStack=function(){ 


};  

registerOnLoad = function(func) {
   var orgOnLoad = startStack;
   startStack = function () {
      orgOnLoad();
      func();
      return;
   }
}


// An additional stack of functions to run after the ajax

theStack=function(){ 


};  

registerOnAjax = function(func) {
   var orgOnAjax = theStack;
   theStack = function () {
     orgOnAjax();
      func();
      return;
   }
}

var ranOnload=false; // Flag to determine if we've ran the starting stack already.

if (document.addEventListener) {
  // Mozilla actually has a DOM READY event.
   document.addEventListener("DOMContentLoaded", function(){if (!ranOnload) {ranOnload=true; startStack();}}, false);
}  else if (document.all && !window.opera) {
  // This is the IE style which exploits a property of the (standards defined) defer attribute
  document.write("<scr" + "ipt id='DOMReady' defer=true " + "src=//:><\/scr" + "ipt>");  
  document.getElementById("DOMReady").onreadystatechange=function(){
    if (this.readyState=="complete"&&(!ranOnload)){
      ranOnload=true;
      startStack();
    }
  }
}

var orgOnLoad=window.onload;
window.onload=function() {
   if (typeof(orgOnLoad)=='function') {
      orgOnLoad();
   }
   if (!ranOnload) {
     ranOnload=true;
     startStack();
   }
}

//onload functions

function addLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (oldonload) {
        oldonload();
      }
      func();
    }
  }
}

function findPos(obj){
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		curleft = obj.offsetLeft
		curtop = obj.offsetTop
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
	}
	return [curleft,curtop];
}


/*
	// The Javascript escape and unescape functions do not correspond
	// with what browsers actually do...
*/

function URLEncode(variable){

	var SAFECHARS = "0123456789" +					// Numeric
					"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +	// Alphabetic
					"abcdefghijklmnopqrstuvwxyz" +
					"-_.!~*'()";					// RFC2396 Mark characters
	var HEX = "0123456789ABCDEF";

	var plaintext = variable;
	var encoded = "";
	for (var i = 0; i < plaintext.length; i++ ) {
		var ch = plaintext.charAt(i);
	    if (ch == " ") {
		    encoded += "+";				// x-www-urlencoded, rather than %20
		} else if (SAFECHARS.indexOf(ch) != -1) {
		    encoded += ch;
		} else {
		    var charCode = ch.charCodeAt(0);
			if (charCode > 255) {
			    alert( "Unicode Character '" 
                        + ch 
                        + "' cannot be encoded using standard URL encoding.\n" +
				          "(URL encoding only supports 8-bit characters.)\n" +
						  "A space (+) will be substituted." );
				encoded += "+";
			} else {
				encoded += "%";
				encoded += HEX.charAt((charCode >> 4) & 0xF);
				encoded += HEX.charAt(charCode & 0xF);
			}
		}
	} // for

	return encoded;
}

/*
JIT Loader
http://www.felocity.org/blog/article/just_in_time_loader_for_javascript/
To provide a lazy loading approach to including Javascrupt
*/

if(!Lock){var Lock=function(){var locks={};var normalize_namespace=function(name){return("c"+name).replace(/[^a-z0-9\-\_]/gi,"")};return{declare:function(){for(var i=0;i<arguments.length;i++){if(!locks[normalize_namespace(arguments[i])]){locks[normalize_namespace(arguments[i])]=new Array()}}},obtain:function(space){var lock=new Object();space=normalize_namespace(space);if(!locks[space]){throw"Namespaces must be declared before getting into locks.";}locks[space].push(lock);if(locks[space][0]===lock){locks[space]=[locks[space][0]]}var lock_obj={isOwner:function(){return(locks[space][0]===lock)},release:function(){if(locks[space][0]===lock){locks[space]=new Array()}}};return lock_obj}}}()}if(!JIT){var JIT=function(){var pending={};var script_ids={};var script_id_counter=0;var lock_owner=null;var script_id_prefix="jit-gen";var loaded_scripts={};var IEVersion=/*@cc_on function(){switch(@_jscript_version){case 1.0:return 3;case 3.0:return 4;case 5.0:return 5;case 5.1:return 5;case 5.5:return 5.5;case 5.6:return 6;case 5.7:return 7}}()||@*/0;var document_head=null;var LOCK_WRITING_TO_DOM="JIT_dom_write";var LOCK_GET_SEQUENCE_ID="JIT_sequence_id";Lock.declare(LOCK_GET_SEQUENCE_ID,LOCK_WRITING_TO_DOM);var generateId=function(){script_id_counter++;return script_id_prefix+script_id_counter};var detectLoadedScripts=function(){var script_nodes=document.getElementsByTagName("script");var css_nodes=document.getElementsByTagName("link");for(var i=0;i<script_nodes.length;i++){var node=script_nodes[i];if(!node.src||node.src.length==0){continue}loaded_scripts[normalizeScriptPath(node.src)]=true}for(var j=0;j<css_nodes.length;j++){var node=css_nodes[j];if(!node.href||node.href.length==0||!node.rel||node.rel.toString().toLowerCase()!="stylesheet"||!node.type||node.type.toString().toLowerCase()!="text/css"){continue}loaded_scripts[normalizeScriptPath(node.href)]=true}};var normalizeScriptPath=function(path){return"s"+escape(path)};var handleLoad=function(urls,verifier,callback,obj,scope,type,once){if(!document.body){window.setTimeout(function(){handleLoad(urls,verifier,callback,obj,scope,type,once)},50);return}var lock=Lock.obtain(LOCK_GET_SEQUENCE_ID);if(!lock.isOwner()){window.setTimeout(function(){handleLoad(urls,verifier,callback,obj,scope,type,once)},10);return}if(!document_head){document_head=document.getElementsByTagName('head')[0]}var sequence_id=generateId();pending[sequence_id]={};script_ids[sequence_id]=[];detectLoadedScripts();lock.release();urls=(urls.constructor===Array)?urls:[urls];if(!verifier||typeof(verifier)!="function"){veifier=function(){return true}}pending[sequence_id]={urls:urls,verifier:verifier,callback:callback,obj:obj,scope:scope,type:type,once:once,lock:lock};if(once){var urls_to_load=[];for(var i=0;i<urls.length;i+=1){var loaded=(loaded_scripts[normalizeScriptPath(urls[i])])?true:false;if(!loaded){urls_to_load.push(urls[i])}}if(urls_to_load.length<=0){loadComplete(sequence_id);return}urls=urls_to_load;pending[sequence_id]={urls:urls,verifier:verifier,callback:callback,obj:obj,scope:scope,type:type,once:once,lock:lock}}if(type=="js"){insertScripts(urls,sequence_id)}else if(type=="css"||(type.match(/^css/i)&&type=="css"+IEVersion)){insertStyles(urls,sequence_id)}else{lock.release();JIT.scriptsComplete(sequence_id)}};var insertStyles=function(urls,sequence_id){urls=urls.constructor===Array?urls:[urls];var node;for(var i=0;i<urls.length;i+=1){var sc_id=generateId();node=document.createElement('link');node.id=sc_id;node.href=urls[i];node.rel="stylesheet";node.type="text/css";node.media="screen";writeNode(node)}if(IEVersion){node.onreadystatechange=function(){if(this.readyState=='loaded'||this.readyState=='complete'){JIT.scriptsComplete(sequence_id)}}}else{var sc_id=generateId();script_ids[sequence_id].push(sc_id);var smart_script=document.createElement('script');smart_script.id=sc_id;smart_script.type="text/javascript";smart_script.appendChild(document.createTextNode("JIT.scriptsComplete('"+sequence_id+"');"));writeNode(smart_script)}if(pending[sequence_id]){pending[sequence_id].lock.release()}};var insertScripts=function(urls,sequence_id){urls=urls.constructor===Array?urls:[urls];var script;for(var i=0;i<urls.length;i+=1){var sc_id=generateId();script_ids[sequence_id].push(sc_id);script=document.createElement('script');script.id=sc_id;script.src=urls[i];script.type="text/javascript";writeNode(script)}if(!script){if(pending[sequence_id]){pending[sequence_id].lock.release()}return}if(IEVersion){script.onreadystatechange=function(){if(this.readyState=='loaded'||this.readyState=='complete'){JIT.scriptsComplete(sequence_id)}}}else{var sc_id=generateId();script_ids[sequence_id].push(sc_id);var smart_script=document.createElement('script');smart_script.id=sc_id;smart_script.type="text/javascript";smart_script.appendChild(document.createTextNode("JIT.scriptsComplete('"+sequence_id+"');"));writeNode(smart_script)}if(pending[sequence_id]){pending[sequence_id].lock.release()}};var writeNode=function(node){var timer=null;var retry_in=100;var processWrite=function(){var lock=Lock.obtain(LOCK_WRITING_TO_DOM);if(!lock.isOwner()){timer=window.setTimeout(processWrite,retry_in);return}window.clearTimeout(timer);timer=null;document_head.appendChild(node);lock.release()};timer=window.setTimeout(processWrite,retry_in)};var loadComplete=function(sequence_id){var timer=null;if(!pending[sequence_id]){return}if(pending[sequence_id].verifier&&!pending[sequence_id].verifier.call(window)){timer=window.setTimeout(function(){loadComplete(sequence_id)},100);return}window.clearTimeout(timer);timer=null;if(pending[sequence_id]){pending[sequence_id].lock.release()}detectLoadedScripts();if(script_ids[sequence_id]){while(script_ids[sequence_id].length>0){var sc_id=script_ids[sequence_id].shift();var script=document.getElementById(sc_id);script.parentNode.removeChild(script)}script_ids[sequence_id]=null}if(pending[sequence_id]&&pending[sequence_id].callback){if(pending[sequence_id].obj){if(pending[sequence_id].scope){pending[sequence_id].callback.call(pending[sequence_id].obj)}else{pending[sequence_id].callback.call(window,pending[sequence_id].obj)}}else{pending[sequence_id].callback.call()}}pending[sequence_id]=null};var JIT_Chain=function(){var stack=[];var run_callback=null;var run_object=null;var run_scope=null;return{load:function(urls,verifier){stack.push({type:"js",once:false,urls:urls,verifier:verifier});return this},loadOnce:function(urls,verifier){stack.push({type:"js",once:true,urls:urls,verifier:verifier});return this},addCSS:function(urls,verifier,ie_version){stack.push({type:"css",once:true,urls:urls,verifier:verifier,ie:ie_version});return this},onComplete:function(callback,obj,scope){var that=this;if(!run_callback){if(!callback){callback=function(){}}run_callback=callback;run_object=obj;run_scpe=scope}if(stack.length==0){if(obj){if(scope){run_callback.call(obj)}else{run_callback.call(window,obj)}}else{run_callback.call()}return}var next_call=stack.shift();if(next_call.type=="js"){if(next_call.once){JIT.loadOnce(next_call.urls,next_call.verifier,that.onComplete,that,true)}else{JIT.load(next_call.urls,next_call.verifier,that.onComplete,that,true)}}else if(next_call.type=="css"){if(next_call.ie){JIT.addCSS(next_call.urls,next_call.verifier,that.onComplete,that,true,next_call.ie)}else{JIT.addCSS(next_call.urls,next_call.verifier,that.onComplete,that,true)}}}}};return{load:function(urls,verifier,callback,obj,scope){handleLoad(urls,verifier,callback,obj,scope,"js",false)},addCSS:function(urls,verifier,callback,obj,scope,ie_restrict){if(!ie_restrict){ie_restrict=""}handleLoad(urls,verifier,callback,obj,scope,"css"+ie_restrict,true)},loadOnce:function(urls,verifier,callback,obj,scope){handleLoad(urls,verifier,callback,obj,scope,"js",true)},startChain:function(){return JIT_Chain()},scriptsComplete:function(sequence_id){loadComplete(sequence_id)}}}()}


/*
Ajaxcaller
http://ajaxify.com/run/testAjaxCaller/
To stndardise AJAX between browsers and use PUT and delete for REST
*/

var ajaxCaller = {

  shouldDebug: false,
  shouldEscapeVars: false,
  shouldMakeHeaderMap: true,

  calls : new Array(),
  pendingResponseCount : 0,

   /**************************************************************************
      PUBLIC METHODS
   *************************************************************************/

  getXML: function(url, callbackFunction) {
    this.get(url, null, callbackFunction, true, null);
  },

  getPlainText: function(url, callbackFunction) {
    this.get(url, null, callbackFunction, false, null);
  },

  postForPlainText: function(url, vars, callbackFunction) {
    this.postVars(url, vars, null, callbackFunction, false,
                    null, "POST", null, null, null);
  },

  postForXML: function(url, vars, callbackFunction) {
    this.postVars(url, vars, null, callbackFunction, true,
                    null, "POST", null, null, null);
  },

  get: function(url, urlVars, callbackFunction, expectingXML, callingContext) {
    this._callServer(url, urlVars, callbackFunction, expectingXML,
                    callingContext, "GET", null, null, null);
  },

  postVars:
    function(url, bodyVars, optionalURLVars, callbackFunction, expectingXML,
             callingContext) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "POST", bodyVars, null, null);
  },

  postBody:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "POST", null, bodyType, body);
  },

  putBody:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "PUT", null, bodyType, body);
  },

  options:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "OPTIONS", null, bodyType, body);
  },

  trace:
    function(url, optionalURLVars, callbackFunction, expectingXML,
             callingContext, bodyType, body) {
      this._debug("trace");
      this._callServer(url, optionalURLVars, callbackFunction, expectingXML,
                      callingContext, "TRACE", null, bodyType, body);
  },

  deleteIt: function(url, urlVars, callbackFunction,
                     expectingXML, callingContext) {
    this._callServer(url, urlVars, callbackFunction, expectingXML,
                    callingContext, "DELETE", null, null, null);
  },

  head: function(url, urlVars, callbackFunction, expectingXML, callingContext)
  {
    this._callServer(url, urlVars, callbackFunction, expectingXML,
                    callingContext, "HEAD", null, null, null);
  },

  /**************************************************************************
     PRIVATE METHODS
  *************************************************************************/

  _callServer: function(url, urlVars, callbackFunction, expectingXML,
                       callingContext, requestMethod, bodyVars,
                       explicitBodyType, explicitBody) {

    if (urlVars==null) {
      urlVars = new Array();
    }

    this._debug("_callServer() called. About to request URL\n"
                + "call key: [" + this.calls.length + "]\n"
                + "url: [" + url + "]\n"
                + "callback function: [" + callbackFunction + "]\n"
                + "treat response as xml?: [" + expectingXML + "]\n"
                + "Request method?: [" + requestMethod + "]\n"
                + "calling context: [" + callingContext + "]\n"
                + "explicit body type: [" + explicitBodyType + "]\n"
                + "explicit body: [" + explicitBody + "]\n"
                + "urlVars: [" + util.describe(urlVars) + "]\n"
                + "bodyVars: [" + util.describe(bodyVars) + "]"
              );


    var xReq = this._createXMLHttpRequest();
    xReq.onreadystatechange = function() {
      ajaxCaller._onResponseStateChange(call);
    }

    var call = {xReq: xReq,
                callbackFunction: callbackFunction,
                expectingXML: expectingXML,
                callingContext: callingContext,
                url: url};

    if (urlVars!=null) {
      var urlVarsString = this._createHTTPVarSpec(urlVars);
      if (urlVarsString.length > 0) { // TODO check if appending with & instead
        url += "?" + urlVarsString;
      }
    }

    xReq.open(requestMethod, url, true);

    if (   requestMethod=="GET"
        || requestMethod=="HEAD"
        || requestMethod=="DELETE") {
      this._debug("Body-less request to URL " + url);
      xReq.send(null);
      return;
    }

    if (   requestMethod=="POST"
        || requestMethod=="PUT"
        || requestMethod=="OPTIONS"
        || requestMethod=="TRACE") {
      bodyType = null;
      body = null;
      if (explicitBodyType==null) { // It's a form
        bodyType = 'application/x-www-form-urlencoded; charset=UTF-8';
        body = this._createHTTPVarSpec(bodyVars);
      } else {
        bodyType = explicitBodyType;
        body = explicitBody;
      }
      this._debug("Content-Type: [" + bodyType + "]\nBody: [" + body + "].");
      xReq.setRequestHeader('Content-Type',  bodyType);
      xReq.send(body);
      return;
    }

    this._debug("ERROR: Unknown Request Method: " + requestMethod);


  },

  // The callback of xmlHttpRequest is a dynamically-generated function which
  // immediately calls this function.
  _onResponseStateChange: function(call) {

    xReq = call.xReq;

    if (xReq.readyState < 4) { //Still waiting
      return;
    }

    if (xReq.readyState == 4) { //Transmit to actual callback
      this._debug("Call " + util.describe(call)
                + " with context [" + call.callingContext+"]"
                + " to " + call.url + " has returned.");
      callbackFunction = call.callbackFunction;
      if (!callbackFunction) { // Maybe still loading, e.g. in another JS file
        setTimeout(function() {
          _onResponseStateChange(call);
        }, 100);
      }
      var content = call.expectingXML ? xReq.responseXML : xReq.responseText;
      responseHeaders = xReq.getAllResponseHeaders();
      headersForCaller = this.shouldMakeHeaderMap ?
        this._createHeaderMap(responseHeaders) : responseHeaders;
      callbackFunction(content, headersForCaller, call.callingContext);
    }

    call = null; // Technically the responsibility of GC
    this.pendingResponseCount--;

  },

  // Browser-agnostic factory function
  _createXMLHttpRequest: function() {
    if (window.XMLHttpRequest) {
      return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
      return new ActiveXObject('Microsoft.XMLHTTP')
    } else {
      _error("Could not create XMLHttpRequest on this browser");
      return null;
    }
  },

  _createHTTPVarSpec: function(vars) {
      var varsString = "";
      for( key in vars ) {
        var value = vars[key];
        if (this.shouldEscapeVars) {
          escapePlusRE =  new RegExp("\\\+");
          value = value.replace(escapePlusRE, "%2B");
        }
        varsString += '&' + key + '=' + value;
      }
      if (varsString.length > 0) {
        varsString = varsString.substring(1); // chomp initial '&'
      }
      this._debug("Built var String: " + varsString)
      return varsString;
   },

  /* Creates associative array from header type to header */
  _createHeaderMap: function(headersText) {
    extractedHeaders = headersText.split("\n");
    delete extractedHeaders[extractedHeaders.length]; // Del blank line at end
    headerMap = new Array();
    for (i=0; i<extractedHeaders.length-2; i++) {
      head = extractedHeaders[i];
      fieldNameEnding = head.indexOf(":");
      field = head.substring(0, fieldNameEnding);
      value = head.substring(fieldNameEnding + 2, head.length);
      value = value.replace(/\s$/, "");
      headerMap[field] = value;
    }
    return headerMap;
  },

  _debug: function(message) {
      if (this.shouldDebug) {
        alert("AjaxJS Message:\n\n" + message);
      }
  },

  _error: function(message) {
      if (this.shouldDebug) {
        alert("AjaxJS ERROR:\n\n" + message);
      }
  }

};


function loadjscssfile(filename, filetype){
 if (filetype=="js"){ //if filename is a external JavaScript file
  var fileref=document.createElement('script')
  fileref.setAttribute("type","text/javascript")
  fileref.setAttribute("src", filename)
 }
 else if (filetype=="css"){ //if filename is an external CSS file
  var fileref=document.createElement("link")
  fileref.setAttribute("rel", "stylesheet")
  fileref.setAttribute("type", "text/css")
  fileref.setAttribute("href", filename)
fileref.setAttribute("media", "print")
 }
 if (typeof fileref!="undefined")
  document.getElementsByTagName("head")[0].appendChild(fileref)
}